﻿#define  _CRT_SECURE_NO_WARNINGS 1

#include "snack.h"

//设置光标位置
void SetPos(short x, short y)
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(Houtput, pos);
}


void HideCursorInfo()
{
	//获得输出句柄
	HANDLE Houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo = { 0 };
	GetConsoleCursorInfo(Houtput, &CursorInfo);
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(Houtput, &CursorInfo);
}


void AdjustConsole()
{
	//调整控制台的大小
	system("mode con cols=100 lines=30");
	//修改控制台的名称
	system("title 贪吃蛇");
}


void Welcome()
{
	SetPos(38, 13);
	wprintf(L"欢迎来到贪吃蛇小游戏");
	SetPos(38, 20);
	system("pause");
}


void PrintHelp()
{
	SetPos(30, 10);
	wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
	SetPos(30, 11);
	wprintf(L"空格（space）表示暂停，Esc表示正常退出游戏");
	SetPos(30, 13);
	wprintf(L"争取获得更高的分数");
	SetPos(30, 22);
	system("pause");
}


void CreateMap(pSnack ps)
{
	//以56*27的大小来创建地图
	//上
	SetPos(0, 0);
	for (int i = 0; i < 56; i += 2)
	{
		wprintf(L"□");
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 56; i += 2)
	{
		wprintf(L"□");
	}
	//左
	for (int i = 1; i < 26; i++)
	{
		SetPos(0, i);
		wprintf(L"□");
	}
	//右
	for (int i = 1; i < 26; i++)
	{
		SetPos(54, i);
		wprintf(L"□");
	}
	//打印出帮助信息
	SetPos(60, 19);
	wprintf(L"↑、↓、←、→分别控制蛇的上下左右移动");
	SetPos(60, 21);
	wprintf(L"空格（space）表示暂停");
	SetPos(60, 23);
	wprintf(L"Esc表示正常退出游戏");
}


pSnackNode BuySnackNode(NodeCoord coord)
{
	pSnackNode newnode = (pSnackNode)malloc(sizeof(SnackNode));
	if (newnode == NULL)
	{
		perror("BuySnackNode()::malloc");
		exit(-1);
	}
	newnode->coord = coord;
	newnode->next = NULL;

	return newnode;
}


void SnackInit(pSnack ps)
{
	//初始蛇有5个结点，假设蛇头的初位置为24,6;
	for (int i = 0; i < 5; i++)
	{
		NodeCoord coord = { POS_X + i * 2, POS_Y };
		if (ps->_SnackHead == NULL)
		{
			ps->_SnackHead = BuySnackNode(coord);
		}
		else
		{
			//头插数据
			pSnackNode newnode = BuySnackNode(coord);
			newnode->next = ps->_SnackHead;
			ps->_SnackHead = newnode;
		}
	}
}


void ShowSnack(pSnack ps)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur == ps->_SnackHead)
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", HEAD);
		}
		else
		{
			SetPos(cur->coord.x, cur->coord.y);
			wprintf(L"%lc", BODY);
		}
		cur = cur->next;
	}
}


void CreateFood(pSnack ps)
{
	int x = 0, y = 0;
	again:
	//食物的坐标要在地图范围内，同时x要是2的倍数
	do
	{
		x = 2 + rand() % (52 - 2 + 1);
		y = 1 + rand() % (25 - 1 + 1);
	} while (x % 2 != 0);

	//坐标不能与蛇身重合
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == x && cur->coord.y == y)
			goto again;
		cur = cur->next;
	}

	NodeCoord FoodPos = { x,y };
	ps->_food = BuySnackNode(FoodPos);

	//显示出食物
	SetPos(ps->_food->coord.x, ps->_food->coord.y);
	wprintf(L"%lc", FOOD);
}


void GameStart(pSnack ps)
{
	//隐藏光标
	HideCursorInfo();
	//调整好控制台的大小和名称
	AdjustConsole();

	//初始化蛇自身的部分
	ps->_state = OK;//状态默认OK
	ps->_dire = RIGHT;//方向默认向右
	ps->_sleeptime = 200;//每走一步中间的间隔时间默认为200ms;
	ps->_SnackHead = NULL;

	//打印欢迎界面
	Welcome();
	system("cls");
	//打印游戏说明界面
	PrintHelp();
	system("cls");
	//创造游戏地图
	CreateMap(ps);

	//初始化蛇身
	SnackInit(ps);
	//打印蛇
	ShowSnack(ps);

	//创造一个食物
	CreateFood(ps);

	//初始化食物的其余数据
	ps->_foodweight = 10;
	ps->_score = 0;
}


void ShowScore(pSnack ps)
{
	SetPos(60, 10);
	wprintf(L"一个食物的分数为%2d", ps->_foodweight);
	SetPos(60, 11);
	wprintf(L"目前总的分数为%2d", ps->_score);

}


void SleepForever()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			//再按一次，继续运动
			break;
		}
	}
}


pSnackNode NextNode(pSnack ps)
{
	pSnackNode nextnode = NULL;
	switch (ps->_dire)
	{
	case UP:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y - 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case DOWN:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x,ps->_SnackHead->coord.y + 1 };
		nextnode = BuySnackNode(coord);
		break;
	}
	case RIGHT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x + 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	case LEFT:
	{
		NodeCoord coord = { ps->_SnackHead->coord.x - 2,ps->_SnackHead->coord.y };
		nextnode = BuySnackNode(coord);
		break;
	}
	}

	return nextnode;
}


bool IsFood(pSnack ps, pSnackNode nextnode)
{
	return ps->_food->coord.x == nextnode->coord.x &&
		   ps->_food->coord.y == nextnode->coord.y;
}


void EatFood(pSnack ps)
{
	//食物成为新一个头结点，蛇的结点增加
	ps->_food->next = ps->_SnackHead;
	ps->_SnackHead = ps->_food;

	//加分
	ps->_score += ps->_foodweight;

	//创造一个新食物
	CreateFood(ps);
}


bool IsBeHitedByWall(pSnackNode nextnode)
{
	return nextnode->coord.x == 0 || nextnode->coord.x == 54 ||
		nextnode->coord.y == 0 || nextnode->coord.y == 26;
}


bool IsBeHitedBySelf(pSnack ps, pSnackNode nextnode)
{
	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		if (cur->coord.x == nextnode->coord.x && cur->coord.y == nextnode->coord.y)
			return true;
		cur = cur->next;
	}

	return false;
}


void Move(pSnack ps, pSnackNode nextnode)
{
	nextnode->next = ps->_SnackHead;
	ps->_SnackHead = nextnode;
	pSnackNode cur = ps->_SnackHead;
	pSnackNode prev = NULL;
	while (cur->next != NULL)
	{
		prev = cur;
		cur = cur->next;
	}
	SetPos(cur->coord.x, cur->coord.y);
	printf("  ");
	free(cur);
	prev->next = NULL;
}


void SnackMove(pSnack ps)
{
	//下一个头结点
	pSnackNode nextnode = NextNode(ps);

	//判断下一个头结点是否是食物
	if (IsFood(ps, nextnode))
	{
		//吃掉食物
		EatFood(ps);
	}
	//判断是否撞墙
	else if (IsBeHitedByWall(nextnode))
	{
		ps->_state = HIT_BYWALL;
	}
	//判断是否撞到自己
	else if (IsBeHitedBySelf(ps, nextnode))
	{
		ps->_state = HIT_BYSELF;
	}
	else
	{
		//正常移动
		Move(ps, nextnode);
	}
}

void GameRun(pSnack ps)
{

	do
	{
		//显示得分情况
		ShowScore(ps);

		if (KEY_PRESS(VK_UP) && ps->_dire != DOWN)
			ps->_dire = UP;
		else if (KEY_PRESS(VK_DOWN) && ps->_dire != UP)
			ps->_dire = DOWN;
		else if (KEY_PRESS(VK_LEFT) && ps->_dire != RIGHT)
			ps->_dire = LEFT;
		else if (KEY_PRESS(VK_RIGHT) && ps->_dire != LEFT)
			ps->_dire = RIGHT;
		else if (KEY_PRESS(VK_SPACE))
			SleepForever();
		else if (KEY_PRESS(VK_ESCAPE))
			ps->_state = END_NORMAL;
		else if (KEY_PRESS(VK_F3))
		{
			//加速，每走一步休眠时间变短，得分更高
			if (ps->_sleeptime > 50)
			{
				ps->_sleeptime -= 30;//不能一直加速，最多加速5次
				ps->_foodweight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//加速，每走一步休眠时间变短，得分更高
			if (ps->_sleeptime < 320)
			{
				ps->_sleeptime += 30;//不能一直加速，最多加速4次
				ps->_foodweight -= 2;
			}
		}

		SnackMove(ps);
		ShowSnack(ps);
		Sleep(ps->_sleeptime);

	} while (ps->_state == OK);

}


void GameOver(pSnack ps)
{
	SetPos(40, 12);
	if (ps->_state == HIT_BYWALL)
		printf("您已撞墙\n");
	else if (ps->_state == HIT_BYSELF)
		printf("您撞到了自己\n");
	else if (ps->_state == END_NORMAL)
		printf("您主动结束了游戏\n");

	pSnackNode cur = ps->_SnackHead;
	while (cur)
	{
		pSnackNode next = cur->next;
		free(cur);
		cur = next;
	}

	free(ps->_food);
	ps->_food = NULL;

}